package devserv

import (
	"encoding/json"
	"event"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"log"
	"message"
	"rhelper"
)

type SwitchMeasureDispatcher struct {
	controller *Controller
}

func (d *SwitchMeasureDispatcher) OnJSONMessage(msgHead *Dev2ServMsgHead, msgBuf []byte) {
	//log.Println("receive msgbuf, prepare to write:",string(msgBuf))
	var encodeBufRuntimeInfo []byte
	encodeBufRuntimeInfo = msgBuf
	switch msgHead.URI {
	case "/report":
		var msg PowerReportMsg
		json.Unmarshal(encodeBufRuntimeInfo, &msg)
		d.controller.SavePowerReportNew(msg)
		log.Println("current report json coent is:", msg)
		break
	case "/ka":
		log.Println("got device keep-alive message")
		type runTimeInfo_dev struct {
			Aqi int `json:"aqi"`
		}
		type runTimeInfo_user struct {
			Aqi int `json:"aqi"`
		}
		var rtiDev runTimeInfo_dev
		var rtiUser runTimeInfo_user
		err := json.Unmarshal(encodeBufRuntimeInfo, &rtiDev)
		if err != nil {
			log.Println("run time info encode failed.", err)
			break
		}

		rtiUser.Aqi = rtiDev.Aqi

		rconn := rhelper.Pool().Get()
		defer rconn.Close()

		rconn.Do("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, "aqi", rtiUser.Aqi)

		c := d.controller
		dt := getCurrentDateTime()
		var kr = ReplyWithDateTime{Error: 0, URI: "/kr"}
		kr.Year = dt.Year
		kr.Month = dt.Month
		kr.Day = dt.Day
		kr.Weekday = dt.Weekday
		kr.MilliSeconds = dt.MilliSeconds
		_ = c.SendJSON(&kr)
		break
	case "/kr":
		log.Println("got keep-alive reply")
		d.controller.add2OnlineDev()
		break
	case "/evtimer":
		type EVTimer struct {
			AName string `json:"aname"` // if "a" means the first action, else "b" means the second.
			Relay string `json:"relay"` // if "open" means relay is open, else "break" means relay is broken
			Id    int    `json:"id"`
		}
		var evtimer EVTimer
		json.Unmarshal(encodeBufRuntimeInfo, &evtimer)
		msg := message.Message{}
		msg.Type = "evtimer"
		msg.To = d.controller.Account
		msg.From = d.controller.Id
		msg.FromName = getControllerAlias(d.controller.Id)
		msg.Abstract = evtimer.AName + ":" + evtimer.Relay
		msg.Title = "Timer task"
		msg.EventId = fmt.Sprintf("%d", evtimer.Id)

		d.controller.updateControllerRelay(evtimer.Relay)

		message.SendMessage(msg, d.controller.Id)
		event.SaveRelayEvent(d.controller.Id, evtimer.Relay, "timer")
		updateTimerStatus(d.controller.Id, msg.EventId, evtimer.AName)

		log.Println("got timer event report:", evtimer)
		break
	case "/state":
		type EVTimer struct {
			Relay string `json:"relay"` // if "open" means relay is open, else "break" means relay is broken
		}
		var evtimer EVTimer
		json.Unmarshal(encodeBufRuntimeInfo, &evtimer)
		msg := message.Message{}
		msg.Type = "state"
		msg.To = d.controller.Account
		msg.From = d.controller.Id
		msg.FromName = getControllerAlias(d.controller.Id)
		msg.Abstract = d.controller.Id + " " + evtimer.Relay
		msg.Title = "state change"

		d.controller.updateControllerRelay(evtimer.Relay)

		event.SaveRelayEvent(d.controller.Id, evtimer.Relay, "manual")
		//message.PublishMessage(d.controller.Account, msgBuf)
		message.SendMessage(msg, d.controller.Id)

		if evtimer.Relay == "open" {
			discardLeftOnWait(d.controller.Id)
			go waitLeftOnNotify(d.controller.Id)
		}
		break
	case "/trigger":
		d.onTrigger(encodeBufRuntimeInfo)
		break
	case "/runtimeInfo":
		//
		// DONE: decode and convert/encode to APP json
		type runTimeInfo_dev struct {
			PowerState       string `json:"powerState"`
			ScreenSleepState string `json:"screenSleepState"`
			AutoState        string `json:"autoState"`
			MoonState        string `json:"moonState"`
			Pwm              int    `json:"pwm"`
			SpeedLeve        int    `json:"speedLeve"`
			Aqi              int    `json:"aqi"`
			FilterUseTime    int    `json:"filterUseTime"`
			FilterUsage      int    `json:"filterUsage"`
			PreFanSpeed      int    `json:"preFanSpeed"`
		}
		type runTimeInfo_user struct {
			PowerState       string  `json:"powerState"`
			ScreenSleepState string  `json:"screenSleepState"`
			AutoState        string  `json:"autoState"`
			MoonState        string  `json:"moonState"`
			Pwm              int     `json:"pwm"`
			SpeedLeve        int     `json:"speedLeve"`
			Aqi              int     `json:"aqi"`
			FilterUseTime    int     `json:"filterUseTime"`
			FilterLife       float64 `json:"filterLife"`
			PreFanSpeed      int     `json:"preFanSpeed"`
			WorkMode         string  `json:"workMode"`
			Online           string  `json:"online"`
		}
		var rtiDev runTimeInfo_dev
		var rtiUser runTimeInfo_user
		err := json.Unmarshal(encodeBufRuntimeInfo, &rtiDev)
		if err != nil {
			log.Println("run time info encode failed.", err)
			break
		}
		rtiUser.PowerState = rtiDev.PowerState
		rtiUser.ScreenSleepState = rtiDev.ScreenSleepState
		rtiUser.AutoState = rtiDev.AutoState
		rtiUser.MoonState = rtiDev.MoonState
		rtiUser.Pwm = rtiDev.Pwm
		rtiUser.SpeedLeve = rtiDev.SpeedLeve
		rtiUser.Aqi = rtiDev.Aqi
		rtiUser.PreFanSpeed = rtiDev.PreFanSpeed
		rtiUser.FilterUseTime = rtiDev.FilterUseTime / 60
		rtiUser.FilterLife = 1.0 - (float64(rtiUser.FilterUseTime) / float64(FilterLifeTotaly))
		rtiUser.Online = "on"

		encodeBufRuntimeInfo, err = json.Marshal(rtiUser)
		if err != nil {
			log.Println("run time info encode err.", err)
		}

		//rc
		rconn := rhelper.Pool().Get()
		defer rconn.Close()
		rconn.Send("MULTI")
		rconn.Send("LPUSH", rhelper.PurifiersStatus+d.controller.Id, string(encodeBufRuntimeInfo))
		rconn.Send("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, "powerState", rtiUser.PowerState, "screenSleepState", rtiUser.ScreenSleepState,
			"autoState", rtiUser.AutoState, "moonState", rtiUser.MoonState, "pwm", rtiUser.Pwm, "speedLeve", rtiUser.SpeedLeve, "aqi", rtiUser.Aqi,
			"preFanSpeed", rtiUser.PreFanSpeed, "filterUseTime", rtiUser.FilterUseTime, "filterLife", rtiUser.FilterLife, "online", rtiUser.Online)
		_, err = rconn.Do("EXEC")
		d.controller.wait <- encodeBufRuntimeInfo
		d.controller.wait = nil
		//log.Println("LPUSH", rhelper.PurifiersStatus + d.controller.Id, string(encodeBufRuntimeInfo))
		//fallthrough
	case "/moon":
		type runTimeInfo_dev struct {
			MoonState        string `json:"moonState"`
			PreFanSpeed      int    `json:"preFanSpeed"`
			ScreenSleepState string `json:"screenSleepState"`
		}
		type runTimeInfo_user struct {
			MoonState        string `json:"moonState"`
			PreFanSpeed      int    `json:"preFanSpeed"`
			AutoState        string `json:"autoState"`
			ScreenSleepState string `json:"screenSleepState"`
		}
		var rtiDev runTimeInfo_dev
		var rtiUser runTimeInfo_user
		err := json.Unmarshal(encodeBufRuntimeInfo, &rtiDev)
		if err != nil {
			log.Println("run time info encode failed.", err)
			break
		}

		rtiUser.MoonState = rtiDev.MoonState
		rtiUser.PreFanSpeed = rtiDev.PreFanSpeed
		rtiUser.ScreenSleepState = rtiDev.ScreenSleepState

		rconn := rhelper.Pool().Get()
		defer rconn.Close()

		rconn.Send("MULTI")

		if rtiDev.MoonState == "on" {
			rtiUser.AutoState = "off"

			rconn.Send("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, "autoState", rtiUser.AutoState)
		}

		encodeBufRuntimeInfo, err = json.Marshal(rtiUser)
		if err != nil {
			log.Println("run time info encode err.", err)
		}

		rconn.Send("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, "moonState", rtiUser.MoonState, "preFanSpeed", rtiUser.PreFanSpeed, "screenSleepState", rtiUser.ScreenSleepState)
		_, err = rconn.Do("EXEC")
		//log.Println("HMSET", rhelper.PurifiersStatusNow + d.controller.Id, "moonState", rtiUser.MoonState, "preFanSpeed", rtiUser.PreFanSpeed)
		d.controller.wait <- encodeBufRuntimeInfo
		d.controller.wait = nil
	//fallthrough
	case "/auto":
		type runTimeInfo_dev struct {
			AutoState        string `json:"autoState"`
			ScreenSleepState string `json:"screenSleepState"`
			PreFanSpeed      int    `json:"preFanSpeed"`
		}
		type runTimeInfo_user struct {
			AutoState        string `json:"autoState"`
			ScreenSleepState string `json:"screenSleepState"`
			PreFanSpeed      int    `json:"preFanSpeed"`
			MoonState        string `json:"moonState"`
		}
		var rtiDev runTimeInfo_dev
		var rtiUser runTimeInfo_user
		err := json.Unmarshal(encodeBufRuntimeInfo, &rtiDev)
		if err != nil {
			log.Println("run time info encode failed.", err)
			break
		}

		rtiUser.AutoState = rtiDev.AutoState
		rtiUser.ScreenSleepState = rtiDev.ScreenSleepState
		rtiUser.PreFanSpeed = rtiDev.PreFanSpeed

		rconn := rhelper.Pool().Get()
		defer rconn.Close()

		rconn.Send("MULTI")

		if rtiDev.AutoState == "on" {
			rtiUser.MoonState = "off"
			rconn.Send("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, "moonState", rtiUser.MoonState)

		}

		encodeBufRuntimeInfo, err = json.Marshal(rtiUser)
		if err != nil {
			log.Println("run time info encode err.", err)
		}

		rconn.Send("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, "autoState", rtiUser.AutoState, "screenSleepState", rtiUser.ScreenSleepState, "preFanSpeed", rtiUser.PreFanSpeed)
		_, err = rconn.Do("EXEC")
		//log.Println("HMSET", rhelper.PurifiersStatusNow + d.controller.Id, "moonState", rtiUser.MoonState, "preFanSpeed", rtiUser.PreFanSpeed)
		d.controller.wait <- encodeBufRuntimeInfo
		d.controller.wait = nil
	//fallthrough
	case "/screen":
		type runTimeInfo_dev struct {
			ScreenSleepState string `json:"screenSleepState"`
		}
		type runTimeInfo_user struct {
			ScreenSleepState string `json:"screenSleepState"`
		}
		var rtiDev runTimeInfo_dev
		var rtiUser runTimeInfo_user
		err := json.Unmarshal(encodeBufRuntimeInfo, &rtiDev)
		if err != nil {
			log.Println("run time info encode failed.", err)
			break
		}

		rtiUser.ScreenSleepState = rtiDev.ScreenSleepState

		rconn := rhelper.Pool().Get()
		defer rconn.Close()

		encodeBufRuntimeInfo, err = json.Marshal(rtiUser)
		if err != nil {
			log.Println("run time info encode err.", err)
		}

		rconn.Do("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, "screenSleepState", rtiUser.ScreenSleepState)
		d.controller.wait <- encodeBufRuntimeInfo
		d.controller.wait = nil
	case "/resetFilter":
		rconn := rhelper.Pool().Get()
		defer rconn.Close()

		rconn.Do("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, "filterUseTime", 0, "filterUsage", 0)

		d.controller.wait <- encodeBufRuntimeInfo
		d.controller.wait = nil
	case "/setFanSpeed":
		type runTimeInfo_dev struct {
			SpeedLeve   int `json:"speedLeve"`
			Pwm         int `json:"pwm"`
			PreFanSpeed int `json:"preFanSpeed"`
		}
		type runTimeInfo_user struct {
			SpeedLeve   int `json:"speedLeve"`
			Pwm         int `json:"pwm"`
			PreFanSpeed int `json:"preFanSpeed"`
		}
		var rtiDev runTimeInfo_dev
		var rtiUser runTimeInfo_user
		err := json.Unmarshal(encodeBufRuntimeInfo, &rtiDev)
		if err != nil {
			log.Println("run time info encode failed.", err)
			break
		}

		rtiUser.SpeedLeve = rtiDev.SpeedLeve
		rtiUser.Pwm = rtiDev.Pwm
		rtiUser.PreFanSpeed = rtiDev.PreFanSpeed

		rconn := rhelper.Pool().Get()
		defer rconn.Close()

		encodeBufRuntimeInfo, err = json.Marshal(rtiUser)
		if err != nil {
			log.Println("run time info encode err.", err)
		}

		rconn.Do("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, "speedLeve", rtiUser.SpeedLeve, "pwm", rtiUser.Pwm, "preFanSpeed", rtiUser.PreFanSpeed, "moonState", "off")
		d.controller.wait <- encodeBufRuntimeInfo
		d.controller.wait = nil
	case "/reportState":
		type runTimeInfo_dev struct {
			Type             int    `json:"type"`
			State            string `json:"state"`
			ScreenSleepState string `json:"screenSleepState"`
		}
		type runTimeInfo_user struct {
			Type             int    `json:"type"`
			State            string `json:"state"`
			ScreenSleepState string `json:"screenSleepState"`
		}
		var rtiDev runTimeInfo_dev
		var rtiUser runTimeInfo_user
		err := json.Unmarshal(encodeBufRuntimeInfo, &rtiDev)
		if err != nil {
			log.Println("run time info encode failed.", err)
			break
		}

		rtiUser.Type = rtiDev.Type
		rtiUser.State = rtiDev.State
		rtiUser.ScreenSleepState = rtiDev.ScreenSleepState

		rconn := rhelper.Pool().Get()
		defer rconn.Close()

		btn := "nil"
		rconn.Send("MULTI")
		switch rtiUser.Type {
		case 1:
			btn = "powerState"
		case 2:
			btn = "screenSleepState"
		case 3:
			btn = "moonState"
			rconn.Send("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, "screenSleepState", rtiUser.ScreenSleepState)
		case 4:
			btn = "autoState"
			rconn.Send("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, "screenSleepState", rtiUser.ScreenSleepState)
		}

		encodeBufRuntimeInfo, err = json.Marshal(rtiUser)
		if err != nil {
			log.Println("run time info encode err.", err)
		}

		rconn.Send("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, btn, rtiUser.State)
		rconn.Do("EXEC")
		sendUpdateMessage(d)
	case "/reportFanState":
		type runTimeInfo_dev struct {
			Leve  int `json:"leve"`
			Speed int `json:"speed"`
		}
		type runTimeInfo_user struct {
			Leve  int `json:"leve"`
			Speed int `json:"speed"`
		}
		var rtiDev runTimeInfo_dev
		var rtiUser runTimeInfo_user
		err := json.Unmarshal(encodeBufRuntimeInfo, &rtiDev)
		if err != nil {
			log.Println("run time info encode failed.", err)
			break
		}

		rtiUser.Leve = rtiDev.Leve
		rtiUser.Speed = rtiDev.Speed

		rconn := rhelper.Pool().Get()
		defer rconn.Close()

		encodeBufRuntimeInfo, err = json.Marshal(rtiUser)
		if err != nil {
			log.Println("run time info encode err.", err)
		}

		rconn.Do("HMSET", rhelper.PurifiersStatusNow+d.controller.Id, "speedLeve", rtiUser.Leve, "pwm", rtiUser.Speed, "moonState", "off")
		sendUpdateMessage(d)
	default:
		//message.PublishMessage(d.controller.Account, msgBuf)
		if d.controller.wait == nil {
			log.Println("no wait channel for message:", msgHead.URI)
		} else {
			//log.Println("c.wait has been writen.", string(msgBuf))
			d.controller.wait <- encodeBufRuntimeInfo
			d.controller.wait = nil
		}
		break
	}
}

func sendUpdateMessage(d *SwitchMeasureDispatcher) {
	type uriMsg struct {
		Uri string `json:"uri"`
	}

	type pubMsg struct {
		Cid     string `json:"cid"`
		ErrCode int    `json:"errCode"`
		MsgBuf  []byte `json:"msgBuf"`
	}
	urimsg := uriMsg{}
	urimsg.Uri = "update"
	urimsgJson, _ := json.Marshal(urimsg)

	pubmsg := pubMsg{}
	pubmsg.Cid = d.controller.Id
	pubmsg.ErrCode = 0
	pubmsg.MsgBuf = urimsgJson
	pubmsgJson, _ := json.Marshal(pubmsg)
	publish("purifiersUserServer", pubmsgJson)
}

func (d *SwitchMeasureDispatcher) OnUserRequest(message []byte) error {

	// log.Printf("write message to controller %s, uri:%s \n", msgHead.ControllerId, msgHead.URI)

	c := d.controller
	return c.SendMessage(message)
}

func (d *SwitchMeasureDispatcher) onTrigger(msgBuf []byte) {
	log.Println("onTrigger:")

	type Trigger struct {
		Cid          string `json:"cid"`
		Above        int    `json:"above"`
		Below        int    `json:"below"`
		IsClosePlug  bool   `json:"isClosePlug"`
		IsSendMail   bool   `json:"isSendMail"`
		IsPushNotify bool   `json:"isPushNotify"`
	}

	type Reply struct {
		Uri  string `json:"uri"`
		Type uint8  `json:"type"`
	}

	var reply Reply
	err := json.Unmarshal(msgBuf, &reply)
	if err != nil {
		log.Println("onChangeTrigger failed, json decode failed:", err)
		return
	}

	var triggerMsg string
	if reply.Type == 1 {
		triggerMsg = "Power is greater than the set value"
	} else {
		triggerMsg = "Power is less than the set value"
	}

	rconn := rhelper.Pool().Get()
	defer rconn.Close()

	cfgStr, err := redis.String(rconn.Do("HGET", rhelper.ControllerHash+d.controller.Id, "trigger"))
	if err != nil && err != redis.ErrNil {
		log.Println(" err:", err)
		return
	}

	var trigger Trigger
	err = json.Unmarshal([]byte(cfgStr), &trigger)
	if err != nil {
		log.Println("onChangeTrigger failed, json decode failed:", err)
		return
	}

	if trigger.IsClosePlug {
		d.controller.updateControllerRelay("break")
		event.SaveRelayEvent(d.controller.Id, "break", "trigger")
	}

	if trigger.IsPushNotify {
		msg := message.Message{}
		msg.Type = "evtrigger"
		msg.To = d.controller.Account
		msg.From = d.controller.Id
		msg.Abstract = "trigger"
		msg.Detail = triggerMsg
		msg.Title = triggerMsg
		msg.FromName = getControllerAlias(d.controller.Id)

		message.SendMessage(msg, d.controller.Id)
	}

	if trigger.IsSendMail {

		uniqueId, err := redis.String(rconn.Do("HGET", rhelper.AccountHash+d.controller.Account, "id"))
		if err != nil {
			log.Println("send email, get unique id failed:", err)
			return
		}

		email, err := redis.String(rconn.Do("HGET", rhelper.UserHash+uniqueId, "email"))
		if err != nil && err != redis.ErrNil {
			log.Println("get user email failed, error:", err)
			return
		}

		if email == "" {
			return
		}

		message.SendGmail(email, triggerMsg)
	}
}

func updateTimerStatus(cid string, planId string, aname string) {
	log.Println("updateTimerStatus")
	type Task struct {
		Cid    string `json:"cid"`
		PlanId int    `json:"planId"`

		StartTime   int `json:"start_time"`
		StartAction int `json:"start_action"`
		Duration    int `json:"duration"`
		EndAction   int `json:"end_action"`

		Year  int `json:"year"`
		Month int `json:"month"`
		Day   int `json:"day"`

		Weekly string `json:"weekly"`
		Status string `json:"status"`
		Utc    string `json:"utc"`
		Type   string `json:"type"`
		Remark string `json:"remark"`
	}

	rconn := rhelper.Pool().Get()
	defer rconn.Close()

	planTaskStr, err := redis.String(rconn.Do("HGET", rhelper.PlanHash+cid, planId))
	if err != nil {
		log.Println("get plan task err: ", err)
	}

	if planTaskStr != "" {
		var planTask Task
		err := json.Unmarshal([]byte(planTaskStr), &planTask)
		if err != nil {
			log.Println("decode json error: ", err)
			return
		}

		if planTask.Weekly != "" {
			return
		}

		if planTask.Type != "timer" && aname != "b" {
			return
		}

		planTask.Status = "false"
		buf, err := json.Marshal(planTask)
		if err != nil {
			log.Println("encod err:", err)
			return
		}
		_, err = rconn.Do("HSET", rhelper.PlanHash+cid, planId, buf)
		if err != nil {
			log.Println("save plan task failed, error:", err)
		}
		return

	} else {
		log.Printf("can't get device %s task %s", cid, planId)
	}

	delayTaskStr, err := redis.String(rconn.Do("HGET", rhelper.ControllerHash+cid, "delayTask"))
	if delayTaskStr == "" {
		return
	}

	var delayTask Task
	err = json.Unmarshal([]byte(delayTaskStr), &delayTask)
	if err != nil {
		return
	}

	delayTask.Duration = 0

	buf, err := json.Marshal(delayTask)
	if err != nil {
		log.Println("encod err:", err)
		return
	}
	_, err = rconn.Do("HSET", rhelper.ControllerHash+cid, "delayTask", buf)
	if err != nil {
		log.Println("save delay task failed, error:", err)
		return
	}

}

func getControllerAlias(cid string) string {
	rconn := rhelper.Pool().Get()
	defer rconn.Close()

	values, err := redis.Strings(rconn.Do("HMGET", rhelper.ControllerHash+cid, "deviceName", "alias"))
	if err != nil {
		log.Println("getControllerAlias, err: ", err)
		return ""
	}

	if values[1] != "" {
		return values[1]
	}

	return values[0]

}
